﻿using System;
using System.Collections.Generic;
using System.Drawing;

namespace ChineseChessPieceRule
{
    /// <summary>
    /// 棋子规则
    /// </summary>
    public static class PieceRule
    {
        private const char 俥 = 'R';
        private const char 車 = 'r';
        private const char 傌 = 'N';
        private const char 馬 = 'n';
        private const char 相 = 'B';
        private const char 象 = 'b';
        private const char 仕 = 'A';
        private const char 士 = 'a';
        private const char 帥 = 'K';
        private const char 將 = 'k';
        private const char 炮 = 'C';
        private const char 砲 = 'c';
        private const char 兵 = 'P';
        private const char 卒 = 'p';

        /// <summary>
        /// 10行
        /// </summary>
        private const int row = 10;
        /// <summary>
        /// 9列
        /// </summary>
        private const int col = 9;

        /// <summary>
        /// 判断棋子颜色
        /// </summary>
        /// <param name="piece"></param>
        /// <returns></returns>
        private static PieceColor GetPieceColor(char piece)
        {
            if (piece >= 'a' && piece <= 'z')
            {
                return PieceColor.Black;
            }
            else if (piece >= 'A' && piece <= 'Z')
            {
                return PieceColor.Red;
            }
            else
            {
                return PieceColor.White;
            }
        }

        /// <summary>
        /// 红方是否在将军
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        private static bool IsRedJiangJun(char[,] board)
        {
            List<int[]> steps = GetRedPiecesMoves(board);
            foreach (var item in steps)
            {
                if (board[item[2], item[3]] == 將)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 黑方是否在将军
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        private static bool IsBlackJiangJun(char[,] board)
        {
            List<int[]> steps = GetBlackPiecesMoves(board);
            foreach (var item in steps)
            {
                if (board[item[2], item[3]] == 帥)
                {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 当前行动方是否在将军
        /// </summary>
        /// <param name="boarad"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        private static bool JiangJun(char[,] boarad, string color)
        {
            if (color.Equals("red", StringComparison.OrdinalIgnoreCase))
            {
                return IsRedJiangJun(boarad);
            }
            else
            {
                return IsBlackJiangJun(boarad);
            }
        }

        /// <summary>
        /// 行动方此举是否在送將
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool SongJiang(char[,] board, int x1, int y1, int x2, int y2)
        {
            PieceColor color = GetPieceColor(board[x1, y1]);

            char[,] tempBoard = (char[,])board.Clone();
            tempBoard[x2, y2] = tempBoard[x1, y1];
            tempBoard[x1, y1] = '0';

            if (color == PieceColor.Red)
            {
                return IsBlackJiangJun(tempBoard);
            }
            else
            {
                return IsRedJiangJun(tempBoard);
            }
        }

        /// <summary>
        /// 返回不送將的所有可行棋步
        /// </summary>
        /// <param name="board"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        public static List<int[]> GetPiecesMovesRight(char[,]board,string color)
        {
            List<int[]> result = new();
            List<int[]> temp = GetPiecesMoves(board, color);
            foreach(var item in temp)
            {
                if (!SongJiang(board, item[0], item[1], item[2], item[3]))
                {
                    result.Add(item);
                }
            }
            return result;
        }


        /// <summary>
        /// 返回某棋局某一方的可行棋步
        /// </summary>
        /// <param name="board"></param>
        /// <param name="color"></param>
        /// <returns></returns>
        private static List<int[]> GetPiecesMoves(char[,] board, string color)
        {
            if (color.Equals("red", StringComparison.OrdinalIgnoreCase))
            {
                return GetRedPiecesMoves(board);
            }
            else
            {
                return GetBlackPiecesMoves(board);
            }
        }

        /// <summary>
        /// 获取某局面下所有红子走法
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        private static List<int[]> GetRedPiecesMoves(char[,] board)
        {
            List<int[]> result = new();
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < col; j++)
                {
                    if (GetPieceColor(board[i, j]) == PieceColor.Red)
                    {
                        result.AddRange(GetPieceMoves(board, i, j));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 获取某局面下所有黑子走法
        /// </summary>
        /// <param name="board"></param>
        /// <returns></returns>
        private static List<int[]> GetBlackPiecesMoves(char[,] board)
        {
            List<int[]> result = new();
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < col; j++)
                {
                    if (GetPieceColor(board[i, j]) == PieceColor.Black)
                    {
                        result.AddRange(GetPieceMoves(board, i, j));
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 某局面下某棋子的所有可行棋步
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <returns></returns>
        private static List<int[]> GetPieceMoves(char[,] board, int x, int y)
        {
            List<int[]> result = new();
            for (int i = 0; i < row; i++)
            {
                for (int j = 0; j < col; j++)
                {
                    if (Try(board, x, y, i, j))
                    {
                        result.Add(new int[] { x, y, i, j });
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// 某局面要将(x1,y1)的棋子移动至(x2,y2)
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool Try(char[,] board, int x1, int y1, int x2, int y2)
        {
            //选中和移动不能是同一点
            if (x1 == x2 && y1 == y2)
            {
                return false;
            }

            //移动的要是棋子才行
            if (GetPieceColor(board[x1, y1]) == PieceColor.White)
            {
                return false;
            }

            //不能吃自己人
            if (GetPieceColor(board[x1, y1]) == GetPieceColor(board[x2, y2]))
            {
                return false;
            }

            return board[x1, y1] switch
            {
                俥 or 車 => TryJu(board, x1, y1, x2, y2),
                傌 or 馬 => TryMa(board, x1, y1, x2, y2),
                相 or 象 => TryXiang(board, x1, y1, x2, y2),
                仕 or 士 => TryShi(x1, y1, x2, y2),
                帥 or 將 => TryKing(board, x1, y1, x2, y2),
                炮 or 砲 => TryPao(board, x1, y1, x2, y2),
                兵 or 卒 => TrySoldier(board, x1, y1, x2, y2),
                _ => throw new Exception("代码格式有误"),
            };
        }

        /// <summary>
        /// 俥是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryJu(char[,] board, int x1, int y1, int x2, int y2)
        {
            //車行直线
            if (x1 != x2 && y1 != y2)
            {
                return false;
            }

            int start, end;
            if (x1 == x2)
            {
                start = Math.Min(y1, y2);
                end = Math.Max(y1, y2);

                for (int i = x1, j = start + 1; j < end; j++)
                {
                    //中间有其他棋子挡住，则不可行
                    if (GetPieceColor(board[i, j]) != PieceColor.White)
                    {
                        return false;
                    }
                }
            }
            else
            {
                start = Math.Min(x1, x2);
                end = Math.Max(x1, x2);

                for (int i = start + 1, j = y1; i < end; i++)
                {
                    //中间有其他棋子挡住，则不可行
                    if (GetPieceColor(board[i, j]) != PieceColor.White)
                    {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// 傌是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryMa(char[,] board, int x1, int y1, int x2, int y2)
        {
            //傌走日
            if ((int)(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2)) != 5)
            {
                return false;
            }

            int o_x, o_y;
            if (x1 - x2 == 2)
            {
                o_x = x1 - 1;
                o_y = y1;
            }
            else if (x1 - x2 == -2)
            {
                o_x = x1 + 1;
                o_y = y1;
            }
            else if (y1 - y2 == 2)
            {
                o_x = x1;
                o_y = y1 - 1;
            }
            else
            {
                o_x = x1;
                o_y = y1 + 1;
            }

            //是否没蹩马腿
            return GetPieceColor(board[o_x, o_y]) == PieceColor.White;
        }

        /// <summary>
        /// 相是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryXiang(char[,] board, int x1, int y1, int x2, int y2)
        {
            //相走田
            if (!(Math.Abs(x1 - x2) == 2 && Math.Abs(y1 - y2) == 2))
            {
                return false;
            }

            //相不过河
            if (x1 <= 4 && x2 >= 5 || x1 >= 5 && x2 <= 4)
            {
                return false;
            }

            int o_x = (x1 + x2) / 2;
            int o_y = (y1 + y2) / 2;
            //是否没塞相眼
            return GetPieceColor(board[o_x, o_y]) == PieceColor.White;
        }

        /// <summary>
        /// 仕是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryShi(int x1, int y1, int x2, int y2)
        {
            //仕走口
            if (!(Math.Abs(x1 - x2) == 1 && Math.Abs(y1 - y2) == 1))
            {
                return false;
            }

            //仕不出九宫
            if (y2 < 3 || y2 > 5 || x2 > 2 && x2 < 7)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 帥是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryKing(char[,] board, int x1, int y1, int x2, int y2)
        {
            //帥將对脸
            if (board[x1, y1] == 帥 && board[x2, y2] == 將 || board[x2, y2] == 帥 && board[x1, y1] == 將)
            {
                //必须同列
                if (y1 != y2)
                {
                    return false;
                }
                for (int i = Math.Min(x1, x2) + 1, j = y1; i < Math.Max(x1, x2); i++)
                {
                    //有其他棋子阻挡
                    if (GetPieceColor(board[i, j]) != PieceColor.White)
                    {
                        return false;
                    }
                }
                //对面笑成立
                return true;
            }
            else
            {
                //帥只能走一步
                if ((int)(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2)) != 1)
                {
                    return false;
                }

                //帥不能出宫
                if (y2 < 3 || y2 > 5 || x2 > 2 && x2 < 7)
                {
                    return false;
                }

                return true;
            }
        }

        /// <summary>
        /// 炮是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TryPao(char[,] board, int x1, int y1, int x2, int y2)
        {
            //像車一样走直线
            if (TryJu(board, x1, y1, x2, y2) && GetPieceColor(board[x2, y2]) == PieceColor.White)
            {
                return true;
            }

            //不是直线
            if (x1 != x2 && y1 != y2)
            {
                return false;
            }

            //炮不能打空地
            if (GetPieceColor(board[x2, y2]) == PieceColor.White)
            {
                return false;
            }

            //中间有且仅有一个子隔着
            int start, end;
            int count = 0;
            if (x1 == x2)
            {
                start = Math.Min(y1, y2);
                end = Math.Max(y1, y2);

                for (int i = x1, j = start + 1; j < end; j++)
                {
                    if (GetPieceColor(board[i, j]) != PieceColor.White)
                    {
                        count++;
                    }
                }
            }
            else
            {
                start = Math.Min(x1, x2);
                end = Math.Max(x1, x2);

                for (int i = start + 1, j = y1; i < end; i++)
                {
                    if (GetPieceColor(board[i, j]) != PieceColor.White)
                    {
                        count++;
                    }
                }
            }

            return count == 1;
        }

        /// <summary>
        /// 兵是否可以这么走
        /// </summary>
        /// <param name="board"></param>
        /// <param name="x1"></param>
        /// <param name="y1"></param>
        /// <param name="x2"></param>
        /// <param name="y2"></param>
        /// <returns></returns>
        private static bool TrySoldier(char[,] board, int x1, int y1, int x2, int y2)
        {
            //兵只能走一格
            if ((int)(Math.Abs(x1 - x2) + Math.Abs(y1 - y2)) != 1)
            {
                return false;
            }

            //兵不能后退
            if (GetPieceColor(board[x1, y1]) == PieceColor.Red && x2 > x1 || GetPieceColor(board[x1, y1]) == PieceColor.Black && x1 > x2)
            {
                return false;
            }

            //未过河前不能平移
            if (GetPieceColor(board[x1, y1]) == PieceColor.Red && x1 == x2 && x1 >= 5 || GetPieceColor(board[x1, y1]) == PieceColor.Black && x1 == x2 && x1 <= 4)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// 棋子颜色
        /// </summary>
        private enum PieceColor
        {
            /// <summary>
            /// 空
            /// </summary>
            White,
            /// <summary>
            /// 红
            /// </summary>
            Red,
            /// <summary>
            /// 黑
            /// </summary>
            Black
        }
    }


}
